package dbmodel

import (
	"crypto/sha1"
	"fmt"
	"github.com/jinzhu/gorm"
	"math/big"
	"math/rand"
	"myCloud/utils"
	"strconv"
	"time"
)

const (
	passwordVersion = 0x31
)

/*
密码方案
1，第一个字节为版本号，当前方案版本为1
2，第2~ qpc2443vpvai
*/
type User struct {
	gorm.Model
	Role      string `gorm:"size:64"`
	Email     string `gorm:"size:128"`
	Phone     string `gorm:"size:128"`
	UserName  string `gorm:"size:128"`
	NickName  string `gorm:"size:128"`
	LastLogin *time.Time
	Password  string `gorm:"size:128"`
	Avatar    string `gorm:"size:512"`
	Stat      string `gorm:"size:16"`
}

func (u *User) TableName() string {
	return "users"
}

func (u *User) GetLastLoginString() string {
	if u.LastLogin == nil {
		return ""
	}

	return utils.Timetostr(*u.LastLogin)
}

// 首先从原始密文中取出 版本号、盐值、哈希值 返回 盐
func extractSalt(pwd string) string {
	if len(pwd) < 4 {
		panic("password length too small")
	}
	ver := pwd[0]
	if ver != passwordVersion {
		panic("password protocol version not match 0x01")
	}
	saltLeft := pwd[1:3]
	saltLen, err := strconv.Atoi(saltLeft)
	if err != nil {
		panic("password salt length format error")
	}
	if len(pwd) < 1+2+saltLen {
		panic("password length not match salt length")
	}
	salt := pwd[3 : 3+saltLen]
	//hval := pwd[3 + saltLen:]
	return salt
}

func getRndSalt() string {
	r1 := rand.Int63()
	b1 := big.NewInt(r1)
	return b1.Text(32)
}

// 将用户密码P1倒序得P2，再加上盐值S1 即 V1=P2+S1 做哈希算法， 得到R1, 在R1 前加1位版本号，2位盐长度，以及盐S1 即得到最终密文
func encodePassowrd(pwd, salt string) string {
	p2 := []byte(pwd)
	for i, j := 0, len(p2)-1; i < j; i, j = i+1, j-1 {
		p2[i], p2[j] = p2[j], p2[i]
	}
	v1 := append(p2, []byte(salt)...)
	h := sha1.New()
	h.Write(v1)
	bs := h.Sum(nil)
	r1 := fmt.Sprintf("%x", bs)

	out := make([]byte, 1+2+len(salt)+len(r1))
	out[0] = passwordVersion
	copy(out[1:], []byte(fmt.Sprintf("%02d", len(salt))))
	copy(out[3:], []byte(salt))
	copy(out[3+len(salt):], r1)
	return string(out)
}

func matchPwd(origin, encodedPwd string) bool {
	salt := extractSalt(encodedPwd)
	rs := encodePassowrd(origin, salt)
	return rs == encodedPwd
}

func (u *User) CheckPwd(pwd string) bool {
	return matchPwd(pwd, u.Password)
}

func (u *User) EncodePassword(pwd string) {
	salt := getRndSalt()
	u.Password = encodePassowrd(pwd, salt)
}
